package com.d.lib.ui.view.vs;

import com.d.lib.ui.view.LogUtil;
import com.d.lib.ui.view.ResourceTable;
import java.io.IOException;
import java.text.DecimalFormat;
import ohos.agp.components.AttrHelper;
import ohos.agp.components.AttrSet;
import ohos.agp.components.Component;
import ohos.agp.render.Canvas;
import ohos.agp.render.Paint;
import ohos.agp.render.Path;
import ohos.agp.render.PixelMapHolder;
import ohos.agp.utils.Color;
import ohos.agp.utils.Rect;
import ohos.agp.utils.RectFloat;
import ohos.agp.utils.TextAlignment;
import ohos.app.Context;
import ohos.global.resource.NotExistException;
import ohos.global.resource.Resource;
import ohos.media.image.ImageSource;
import ohos.media.image.PixelMap;
import ohos.multimodalinput.event.TouchEvent;

/**
 * Use:
 * VSItem vsItemA = new VSItem("A", true); // true: 已选中, false: 未选中
 * VSItem vsItemB = new VSItem("B", false);
 * vsView.setCompareA(vsItemA)
 * vsView.setCompareB(vsItemB)
 * vsView.setPercent(0.85f, true); // param1: 若为50%请传-1; param2: false为只更值不刷新，true为更值并刷新view
 * <p>
 * Created by D on 2017/2/28.
 */
public class VSView extends Component implements Component.DrawTask,
    Component.EstimateSizeListener, Component.TouchEventListener {
  /**
   * Math.pow(...) is very expensive, so avoid calling it and create it
   * yourself.
   */
  private static final int POW_10[] = {
      1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000
  };
  private int mWidth; // View的宽度
  private int mHeight; // View的高度
  private float mPadding; // 对比条距两端间距
  private float mHPercent; // 对比条高度
  private float mRadius; // 对比条的圆角矩形弧度
  private Rect mRect;
  private RectFloat mRectF;
  private Paint mPaintA; // A类颜色的画笔
  private Paint mPaintB; // B类颜色的画笔
  private Paint mPaintTxt; // 仅用于绘制文字的画笔
  private Path mPath; // 通用路径
  private float mMargin; // 两圆与对比条的垂直间距
  private float mMarginTxt; // 文字与圆的水平间距
  private OnVSClickListener mListener; // Listener
  private float mPercent = 0.5f; // 对比A项所占百分比 范围0-1
  private int mDIndex = -1; // ActionDown按压时的位置
  private int mUIndex = 0; // ActionUp松开时的位置
  private int mTouchSlop; // 最小视为移动距离
  private float mDX, mDY; // ActionDown的坐标(dx,dy)
  private boolean mDInvaild; // 标志位,点击是否有效（true有效: 点中了圆, false无效: 未点中圆）
  private float mSpaceHalf; // 对比条，中间空隙的一半宽度
  private float mTanW; // 对比条，中间空隙的偏斜宽度
  private PixelMap mBitmapA; // 对比A项正常图片
  private PixelMap mBitmapAP; // 对比A项按压图片
  private PixelMap mBitmapAS; // 对比A项选中图片
  private PixelMap mBitmapAU; // 对比A项未选中图片
  private PixelMap mBitmapB; // 对比B项正常图片
  private PixelMap mBitmapBP; // 对比B项按压图片
  private PixelMap mBitmapBS; // 对比B项选中图片
  private PixelMap mBitmapBU; // 对比B项未选中图片
  private RectFloat mRectBp; // 仅用于图片Rect
  private VSItem mItemA; // 左项
  private VSItem mItemB; // 右项

  public VSView(Context context) {
    this(context, null);
  }

  public VSView(Context context, AttrSet attrs) {
    this(context, attrs, 0);
  }

  public VSView(Context context, AttrSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    init(context);
  }

  public VSView(Context context, AttrSet attrs, String defStyleAttr) {
    super(context, attrs, defStyleAttr);
    init(context);
  }

  /**
   * format a number properly with the given number of digits
   *
   * @param number the number to format
   * @param digits the number of digits
   */
  public static String formatDecimal(double number, int digits) {
    number = roundNumber((float) number, digits);
    StringBuffer a = new StringBuffer();
    for (int i = 0; i < digits; i++) {
      if (i == 0)
        a.append(".");
      a.append("0");
    }
    DecimalFormat nf = new DecimalFormat("###,###,###,##0" + a.toString());
    String formatted = nf.format(number);
    return formatted;
  }

  public static float roundNumber(float number, int digits) {
    try {
      if (digits == 0) {
        int r0 = (int) Math.round(number);
        return r0;
      } else if (digits > 0) {
        if (digits > 9) {
          digits = 9;
        }
        StringBuffer a = new StringBuffer();
        for (int i = 0; i < digits; i++) {
          if (i == 0)
            a.append(".");
          a.append("0");
        }
        DecimalFormat nf = new DecimalFormat("#" + a.toString());
        String formatted = nf.format(number);
        return Float.valueOf(formatted);
      } else {
        digits = -digits;
        if (digits > 9) {
          digits = 9;
        }
        int r2 = (int) (number / POW_10[digits] + 0.5);
        return r2 * POW_10[digits];
      }
    } catch (NumberFormatException e) {
      e.printStackTrace();
      return number;
    }
  }

  private void init(Context context) {
    int textSize = AttrHelper.fp2px(13, context);
    mPadding = AttrHelper.fp2px(2, context);
    mHPercent = AttrHelper.fp2px(3, context);
    mTanW = AttrHelper.fp2px(0.5f, context);
    mSpaceHalf = AttrHelper.fp2px(1, context);
    mMargin = AttrHelper.fp2px(4, context);
    mMarginTxt = AttrHelper.fp2px(6, context);
    mRadius = AttrHelper.fp2px(13.5f, context);

    mTouchSlop = 24;

    mBitmapA = getPixelMap(ResourceTable.Media_lib_ui_view_vs_icon);
    mBitmapAP = getPixelMap(ResourceTable.Media_lib_ui_view_vs_icon);
    mBitmapAS = getPixelMap(ResourceTable.Media_lib_ui_view_vs_icon);
    mBitmapAU = getPixelMap(ResourceTable.Media_lib_ui_view_vs_icon);
    mBitmapB = getPixelMap(ResourceTable.Media_lib_ui_view_vs_icon);
    mBitmapBP = getPixelMap(ResourceTable.Media_lib_ui_view_vs_icon);
    mBitmapBS = getPixelMap(ResourceTable.Media_lib_ui_view_vs_icon);
    mBitmapBU = getPixelMap(ResourceTable.Media_lib_ui_view_vs_icon);

    mRectBp = new RectFloat();
    mRect = new Rect();
    mRectF = new RectFloat();

    mPath = new Path();

    int colorTxt = Color.getIntColor("#7c838a");

    mPaintTxt = new Paint();
    mPaintTxt.setTextSize(textSize);
    mPaintTxt.setTextAlign(TextAlignment.LEFT);
    mPaintTxt.setColor(new Color(colorTxt));
    mPaintTxt.setAlpha(0xbf);

    mPaintA = new Paint();
    mPaintA.setColor(new Color(Color.getIntColor("#E3542B")));
    mPaintB = new Paint();
    mPaintB.setColor(new Color(Color.getIntColor("#4ABC00")));

    mItemA = new VSItem("", false);
    mItemB = new VSItem("", false);

    calcPercent();

    addDrawTask(this::onDraw);
    setEstimateSizeListener(this::onEstimateSize);
    setTouchEventListener(this);
  }

  /**
   * 通过资源ID获取位图对象.
   **/
  private PixelMap getPixelMap(int mediaId) {
    Resource bgResource = null;
    PixelMap pm = null;
    try {
//            //获取Media文件夹中的图片资源
      bgResource = getResourceManager().getResource(mediaId);
//            // 创建图像数据源ImageSource对象
      ImageSource.SourceOptions srcOpts = new ImageSource.SourceOptions();
      srcOpts.formatHint = "image/png";
      ImageSource imageSource = ImageSource.create(bgResource, srcOpts);
      pm = imageSource.createPixelmap(null);


    } catch (IOException | NotExistException e) {
      e.printStackTrace();
    }
    return pm;
  }

  @Override
  public void onDraw(Component component, Canvas canvas) {
    if (mHeight == 0 || mWidth == 0) {
      return;
    }
    if (mPercent == 0) {
      drawRoundRect(canvas, mRect, mRectF, mPaintB, mHPercent / 2,
          mPadding, mHeight - mHPercent, mWidth - mPadding, mHeight);
    } else if (mPercent == 1) {
      drawRoundRect(canvas, mRect, mRectF, mPaintA, mHPercent / 2,
          mPadding, mHeight - mHPercent, mWidth - mPadding, mHeight);
    } else {
      // 左线
      drawRoundRect(canvas, mRect, mRectF, mPaintA, mHPercent / 2,
          mPadding, mHeight - mHPercent, mPadding + mHPercent, mHeight);
      // 右线
      drawRoundRect(canvas, mRect, mRectF, mPaintB, mHPercent / 2,
          mWidth - mHPercent - mPadding, mHeight - mHPercent, mWidth - mPadding, mHeight);

      float offset = (mWidth - mPadding * 2 - mHPercent) * mPercent + mPadding + mHPercent / 2;
      if (offset < mPadding + mHPercent + mSpaceHalf + mTanW) {
        // 限定最小值
        offset = mPadding + mHPercent + mSpaceHalf + mTanW;
      } else if (offset > mWidth - mPadding - mHPercent - mSpaceHalf - mTanW) {
        // 限定最大值
        offset = mWidth - mPadding - mHPercent - mSpaceHalf - mTanW;
      }

      float left = mPadding + mHPercent / 2;
      float right = offset - mSpaceHalf;
      // 长度
      drawPath(canvas, mPath, mPaintA,
          left, left, right + mTanW, right - +mTanW,
          mHeight - mHPercent, mHeight, mHeight - mHPercent, mHeight);

      left = offset + mSpaceHalf;
      right = mWidth - mPadding - mHPercent / 2;
      drawPath(canvas, mPath, mPaintB,
          left + mTanW, left - mTanW, right, right,
          mHeight - mHPercent, mHeight, mHeight - mHPercent, mHeight);
    }

    float cy = mHeight - mHPercent - mMargin - mRadius;
    drawBitmap(canvas, cy);

    LogUtil.debug("progress", " onDraw mHeight: " + mHeight + ", mHPercent: " + mHPercent +
        "mMargin: "
        + mMargin + ", mRadius: " + mRadius);
    float textHeight = getTextHeight(mPaintTxt);
    mPaintTxt.setTextAlign(TextAlignment.LEFT);
    mPaintTxt.setColor(Color.GRAY);
    LogUtil.debug("progress",
        " onDraw mItemA.mainText: " + mItemA.mainText + ", mItemA.percent: " + mItemA.percent +
            "mRadius: " + mRadius + ", mMarginTxt: " + mMarginTxt);
    canvas.drawText(mPaintTxt, mItemA.mainText + " " + mItemA.percent, mRadius * 2 + mMarginTxt,
        cy + textHeight / 2);
    mPaintTxt.setTextAlign(TextAlignment.RIGHT);
    canvas.drawText(mPaintTxt, mItemB.mainText + " " + mItemB.percent,
        mWidth - mRadius * 2 - mMarginTxt, cy + textHeight / 2);
  }

  private void drawBitmap(Canvas canvas, float cy) {
    PixelMap bpA = mBitmapA, bpB = mBitmapB;
    if (!mItemA.isChecked && !mItemB.isChecked) {
      // A、B项都未选中
      if (mDInvaild) {
        if (mDIndex == 0) {
          bpA = mBitmapAP;
          bpB = mBitmapB;
        } else if (mDIndex == 1) {
          bpA = mBitmapA;
          bpB = mBitmapBP;
        }
      }
    } else {
      if (mItemA.isChecked) {
        bpA = mBitmapAS;
        bpB = mBitmapBU;
      } else {
        bpA = mBitmapAU;
        bpB = mBitmapBS;
      }
    }
    mRectBp.modify(0, (int) (cy - mRadius), (int) (mRadius * 2), (int) (cy + mRadius));
    canvas.drawPixelMapHolderRect(new PixelMapHolder(bpA), mRectBp, new Paint());
    mRectBp.modify((int) (mWidth - mRadius * 2), (int) (cy - mRadius), mWidth,
        (int) (cy + mRadius));
    canvas.drawPixelMapHolderRect(new PixelMapHolder(bpB), mRectBp, new Paint());
  }

  private void drawPath(Canvas canvas, Path path, Paint paint,
                        float ltX, float lbX, float rtX, float rbX,
                        float ltY, float lbY, float rtY, float rbY) {
    path.reset();
    path.moveTo(ltX, ltY);
    path.lineTo(lbX, lbY);
    path.lineTo(rbX, rbY);
    path.lineTo(rtX, rtY);
    path.close();
    canvas.drawPath(path, paint);
  }

  /**
   * Draw圆角矩形
   */
  private void drawRoundRect(Canvas canvas, Rect rect, RectFloat rectF, Paint paint, float rxy,
                             float left, float top, float right, float bottom) {
    rect.set((int) left, (int) top, (int) right, (int) bottom);
    rectF.modify(rect);
    canvas.drawRoundRect(rectF, rxy, rxy, paint);
  }

  public boolean onEstimateSize(int widthMeasureSpec, int heightMeasureSpec) {
    mWidth = EstimateSpec.getSize(widthMeasureSpec);
    mHeight = EstimateSpec.getSize(heightMeasureSpec);
    setEstimatedSize(EstimateSpec.getSizeWithMode(mWidth, EstimateSpec.PRECISE),
        EstimateSpec.getSizeWithMode(mHeight, EstimateSpec.PRECISE));
    return true;
  }

  @Override
  public boolean onTouchEvent(Component component, TouchEvent touchEvent) {
    float eX = touchEvent.getPointerPosition(touchEvent.getIndex()).getX();
    float eY = touchEvent.getPointerPosition(touchEvent.getIndex()).getY();
    switch (touchEvent.getAction()) {
      case TouchEvent.PRIMARY_POINT_DOWN:
        mDX = touchEvent.getPointerPosition(touchEvent.getIndex()).getX();
        mDY = touchEvent.getPointerPosition(touchEvent.getIndex()).getY();
        float cline = mHeight - mHPercent - mMargin;
        if (eX >= 0 && eX <= mRadius * 2 && eY >= cline - mRadius * 2 && eY <= cline) {
          mDIndex = 0;
          mDInvaild = true; // 点击有效
        } else if (eX >= mWidth - mRadius * 2 && eX <= mWidth && eY >= cline - mRadius * 2 && eY <= cline) {
          mDIndex = 1;
          mDInvaild = true; // 点击有效
        } else {
          mDIndex = -1;
          mDInvaild = false; // 点击无效
        }
        invalidate();
        return true;
      case TouchEvent.POINT_MOVE:
        if (Math.abs(eX - mDX) > mTouchSlop || Math.abs(eY - mDY) > mTouchSlop) {
          mDInvaild = false; // 点击无效
          invalidate();
        }
        return true;
      case TouchEvent.PRIMARY_POINT_UP:
        if (mDInvaild) {
          float clineF = mHeight - mHPercent - mMargin;
          if (eX >= 0 && eX <= mRadius * 2 && eY >= clineF - mRadius * 2 && eY <= clineF) {
            mUIndex = 0;
          } else if (eX >= mWidth - mRadius * 2 && eX <= mWidth && eY >= clineF - mRadius * 2 && eY <= clineF) {
            mUIndex = 1;
          }
          if (mUIndex == mDIndex) {
            if (mListener != null) {
              if (mUIndex == 0) {
                mListener.onItemClick(mUIndex, mItemA);
              } else if (mUIndex == 1) {
                mListener.onItemClick(mUIndex, mItemB);
              }
            }
          }
        }
        // Reset
        mDX = 0;
        mDY = 0;
        mDIndex = -1;
        mDInvaild = false;
        invalidate();
        return true;
      case TouchEvent.CANCEL:
        // Reset
        mDX = 0;
        mDY = 0;
        mDIndex = -1;
        mDInvaild = false;
        invalidate();
        break;
    }
    return true;
  }

  private void calcPercent() {
    if (mPercent == -1) {
      mItemA.percent = mItemB.percent = "";
      mPercent = 0.5f;
    } else {
      mItemA.percent = formatDecimal(mPercent * 100, 2) + "%";
      mItemB.percent = formatDecimal(100 - mPercent * 100, 2) + "%";
    }
  }

  public VSView setCompareA(VSItem item) {
    this.mItemA = item;
    return this;
  }

  public VSView setCompareB(VSItem item) {
    this.mItemB = item;
    return this;
  }

  /**
   * 动态刷新-设置对比项A所占百分比并重新绘制-格式为0.00
   */
  public void setPercent(float percentA, boolean isRefresh) {
    mPercent = percentA;
    calcPercent();
    if (isRefresh) {
      invalidate();
    }
  }

  public void setOnVSClickListener(OnVSClickListener l) {
    this.mListener = l;
  }

  public interface OnVSClickListener {
    void onItemClick(int index, VSItem item);
  }

  public static float getTextHeight(Paint p) {
    Paint.FontMetrics fm = p.getFontMetrics();
    return (float) ((Math.ceil(fm.descent - fm.top) + 2.0D) / 2.0D);
  }
}